


package body Scenes is

   function New_Scene (Largeur, Hauteur : Natural; Gravite : Vecteur_Type) return Scene_Type is
      S : Scene_Type;
   begin
      S.Hauteur := long_float (Hauteur);
      S.Largeur := long_float (Largeur);
      S.Gravite := Gravite;
      return S;
   end New_Scene;


   function Get_Height (Scene : Scene_Type) return long_float is
   begin
      return Scene.Hauteur;
   end Get_Height;
   pragma Inline (Get_Height);

   function Get_Width (Scene : Scene_Type) return long_float is
   begin
      return Scene.Largeur;
   end Get_Width;
   pragma Inline (Get_Width);

   function Get_Gravite (This : Scene_Type) return Vecteur_Type is
   begin
      return This.Gravite;
   end Get_Gravite;
   pragma Inline (Get_Gravite);

   function Get_Nb_Subdiv (This : Scene_Type) return Natural is
   begin
      return This.Nb_Subdiv;
   end Get_Nb_Subdiv;
   pragma Inline (Get_Nb_Subdiv);

   function Get_Grille (This : Scene_Type) return T_Grille is
   begin
      return This.Grille;
   end Get_Grille;
   pragma Inline (Get_Grille);

   function Get_Nb_Objets (This : Scene_Type) return Natural is
   begin
      return Natural(This.Objets.Length);
   end Get_Nb_Objets;
   pragma Inline(Get_Nb_Objets);

   function Get_Objet (This : Scene_Type; Indice : Natural) return Bille_Type is
   begin
      return This.Objets.Element (Indice);
   end Get_Objet;

   procedure Remplacer_Objet (This : in out Scene_Type; Indice : Natural; O : Bille_Type) is
   begin
      This.Objets.Replace_Element (Indice, O);
   end Remplacer_Objet;

   procedure Calculer_Bornes (This : Scene_Type; O : in out Bille_Type; Dt : long_float) is
      X     : long_float := O.Pos.X;
      Y     : long_float := O.Pos.Y;
      X2    : long_float := X + O.Vitesse.X * Dt;
      Y2    : long_float := Y + O.Vitesse.Y * Dt;
      L     : long_float := Get_Width (This) / long_float (This.Nb_Subdiv);
      H     : long_float := Get_Height (This) / long_float (This.Nb_Subdiv);
      H_Max : Natural := This.Nb_Subdiv - 1;
      L_Max : Natural := This.Nb_Subdiv - 1;
      R     : T_Rect renames O.Rect;
   begin
      -- calcul des x
      if X2 >= X then
         R.Xmin := Natural (long_float'Max (0.0, long_float'Floor ((X - O.Rayon) / L)));
         R.Xmax := Natural'Min (L_Max, Natural (long_float'Floor ((X2 + O.Rayon) / L)));
      else
         R.Xmin := Natural'Min(Natural(Get_Width(This)),Natural (long_float'Max (0.0, long_float'Floor ((X2 - O.Rayon) / L))));
         R.Xmax := integer'Max(0, integer'Min (L_Max, integer (long_float'Floor ((X + O.Rayon) / L))));
      end if;

      -- calcul des y
      if Y2 >= Y then
         R.Ymin := Natural (long_float'Max (0.0, long_float'Floor ((Y - O.Rayon) / H)));
         R.Ymax :=  Natural'Min (H_Max, Natural (long_float'Floor ((Y2 + O.Rayon) / H)));
      else
         R.Ymin := Natural (long_float'Max (0.0, long_float'Floor ((Y2 - O.Rayon) / H)));
         R.Ymax :=  Natural'Min (H_Max, Natural (long_float'Floor ((Y + O.Rayon) / H)));
      end if;
   end Calculer_Bornes;


   procedure Ajouter (This : in out Scene_Type; Objet : Bille_Type) is
      Obj : Bille_Type := Objet;
   begin
      Obj.Acceleration := This.Gravite;
      This.Objets.Append (Obj);
   end Ajouter;


   procedure Remplir_Grille (S : in out Scene_Type; Dt : Long_Float) is
      O        : Bille_Type;
   begin
      for I in 1 .. S.Get_Nb_Objets loop
	 O := S.Objets.Element (I);
	 Calculer_Bornes (S, O, Dt);
	 S.Objets.Replace_Element (I, O);

	 for X in O.Rect.Xmin .. O.Rect.Xmax loop
	    for Y in O.Rect.Ymin .. O.Rect.Ymax loop
	       S.Grille (X, Y).Insert (I);
	    end loop;
	 end loop;

      end loop;
   end Remplir_Grille;

   procedure Remplir_Grille (S : in out Scene_Type; Id : Natural ; Dt : Long_Float) is
      O        : Bille_Type := S.Get_Objet (Id);
   begin
      Calculer_Bornes (S, O, Dt);
      S.Objets.Replace_Element (Id, O);

      for X in O.Rect.Xmin .. O.Rect.Xmax loop
	 for Y in O.Rect.Ymin .. O.Rect.Ymax loop
	    S.Grille (X, Y).Insert (Id);
	 end loop;
      end loop;
   end Remplir_Grille;

   procedure Effacer_Grille (S : in out Scene_Type ) is
   begin
      for I in S.Grille'Range (1) loop
	 for J in S.Grille'Range (2) loop
	    S.Grille (I, J).Clear;
	 end loop;
      end loop;
   end Effacer_Grille;

   procedure Supprimer_Id_Grille (S : in out Scene_Type; Id : Natural) is
      O : Bille_Type := S.Get_Objet (Id);
   begin
      for X in O.Rect.Xmin .. O.Rect.Xmax loop
	 for Y in O.Rect.Ymin .. O.Rect.Ymax loop
	    S.Grille (X, Y).Delete (Id);
	 end loop;
      end loop;
   end Supprimer_Id_Grille;


end Scenes;
